package tool.csv;

import static tool.Workbench.ENCODING;

import java.io.BufferedWriter;
import java.io.IOException;
//import java.nio.charset.Charset;
//import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;


/**
 * @author jwb09119
 * @date 2013/11/15
 * 
 * This class is responsible for writing the results of experimentation to a file.
 * The output format will be Comma Separated Values (CSV) for the moment.
 * 
 * To avoid any messing around with opening and closing of files (by external users), I assume that
 * the FileWriter will be passed to other entities that know how to export their own data, in this
 * sense this class is composed of mostly static methods as it has very little internal state.
 * 
 * NB - not sure about the static import from Workbench - though I do want to have a single point of
 * reference for file operation constants such as ENCODING... 
 * 
 */

public class FileWriter {
	
	public final char SEPARATOR = ',';
	
	//public final static Charset ENCODING = StandardCharsets.UTF_8;
	private Path homeDirectory;
	private int fileCount;
	private static Path logFile;
	private BufferedWriter currentFileWriter;

	
	
	public FileWriter (Path homeDirectory) {
		
		this.homeDirectory = homeDirectory;
		fileCount = 0;
		
		logFile = Paths.get(homeDirectory.toString() + "FileWriterLog.log");
		initialiseFile(logFile);
		
		currentFileWriter = null;
		
	}
	
	
	public void dumpObjectToCSVFile (CSVCompatible entity, String comment) {
		
		entity.exportResults(comment);
		
	}


	public void startNewExport() {

		if(currentFileWriter!= null) {
			
			notifyEndOfExport();
			
		}
		
		Path newFile = getNewFilePath();
		initialiseFile(newFile);
		writeToLogFile("Starting Export: "+newFile.getFileName());
		
		try {
			
			currentFileWriter = Files.newBufferedWriter(newFile, ENCODING, 
																		StandardOpenOption.APPEND);
			
		} catch (IOException e) {
			
			System.err.println("new export error: \n" + e);
			e.printStackTrace();
			
		}
		
	}


	/**
	 * Writes an arbitrary number of items to file as one delimited line
	 * @param items - vararg String - one or more Strings
	 */
	public void writeComment(String... items) {

		if (currentFileWriter == null) {
		
			writeToLogFile("Line written with no destination: \"" + items +"\"");
		
		} else {
				
			StringBuilder line = new StringBuilder();
			
			for (int i = 0; i < items.length ; i++) {
				
				line.append(items[i]);
				
				if (i < items.length-1) {
					line.append(SEPARATOR);
				}
				
			}
			
			try {
				
				currentFileWriter.write(line.toString());
				currentFileWriter.newLine();
				
			} catch (IOException e) {
				
				System.err.println("Write comment error: \n" + e);
				e.printStackTrace();
				
			}
			
		}
	
	}


	public void notifyEndOfExport() {

		writeToLogFile("Finishing Export");
		try {
			
			currentFileWriter.close();
			
		} catch (IOException e) {
			
			System.err.println("Error closing file: \n" + e);
			e.printStackTrace();
		}
		currentFileWriter = null;
		
	}
	
	
	public static void writeToLogFile (String writeThis) {
		
		ArrayList<String> line = new ArrayList<>();
		line.add(writeThis);
		
		try {
			
			Files.write(logFile, line, ENCODING, StandardOpenOption.APPEND);
			
		} catch (IOException e) {
			System.err.println("Error Writing ot log file: \n" + e);
			e.printStackTrace();
			
		}
		
	}
	
	
	/**
	 * File Operations Stuff
	 */
	

	private Path getNewFilePath () {
		
		Path newPath = Paths.get(homeDirectory.toString() + "" + fileCount + ".txt");
		fileCount++;
		
		return newPath;
		
	}


	/**
	 * Creates a file for a specific path and file name if the file does not already exist.
	 * @param file
	 */
	private void initialiseFile(Path file) {
		
		if(!file.toFile().exists()) {
		
			try {
	
			    Files.createFile(file);
			    
			} catch (FileAlreadyExistsException x) {
				
			    System.err.println("file named " + file + " already exists");
			    
			} catch (IOException x) {
				
			    System.err.println("initialiseFile error: \n" + x);
			    
			}
			
		}
		
	}


//	/**
//	 * Convert a List of String to a CSV compatible value
//	 * @param listOfStrings - a List of Strings {a, b, c}
//	 * @return - String- representing listOfStrings "(A,B,C)"
//	 */
//	public final String toCSVFormat(List<String> listOfStrings) {
//		
//		StringBuilder buffer = new StringBuilder();
//		
//		buffer.append('(');
//		for (String s : listOfStrings) {
//			buffer.append(s);
//			buffer.append(SEPARATOR);
//		}
//		if (buffer.length() > 1) {
//			buffer.deleteCharAt(buffer.length()-1);
//		}
//		buffer.append(')');
//		
//		return buffer.toString();
//		
//	}
	
    /**
      * Convert a List of Objects to a CSV compatible value
      * @param listOfThings - a List of Objects {a, b, c}
      * @return - String- representing listOfThings "(A,B,C)"
      * 
      * NB - this method have been made type-less as it was defined for other types - see example
      *      above, then would not compile as the methods were Identical after type erasure.  So
      *      long as the objects fed in have a sensible toString() method, everything is fine...
      */
	public final String toCSVFormat(List<?> listOfThings) {
	    
	       StringBuilder buffer = new StringBuilder();
	        
	        buffer.append('(');
	        for (Object s : listOfThings) {
	            buffer.append(s);
	            buffer.append(SEPARATOR);
	        }
	        if (buffer.length() > 1) {
	            buffer.deleteCharAt(buffer.length()-1);
	        }
	        buffer.append(')');
	        
	        return buffer.toString();
	    
	}
	
}
